#include <stdarg.h>
#include "stdlib.h"

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "FreeRTOSConfig.h"

#include "uart.h"
#include "LPC17xx.h"
#include "pin_config.h"
#include "clock.h"
#include "interrupt.h"
#include "user_interface.h"


/* FreeRTOS variables */
xQueueHandle xUart_Queue;
xSemaphoreHandle xUart_Semaphore;

/**
 * initialize UART 0
 *
 * @param  uint32_t baudrate UART0 baudrate
 * @return none
 *
 * @brief  initialize uart0
 *
 *
 */
void uart_init(uint32_t baudrate)
{
    unsigned long divider;

    xUart_Queue = xQueueCreate(128 , sizeof(int8_t*));

    vSemaphoreCreateBinary( xUart_Semaphore );

    if( xUart_Semaphore != NULL )
    {
        // The semaphore was created successfully.
        // The semaphore can now be used.
    }

    /* Turn on power to UART0 */
    SC->PCONP |= PCUART0_POWERON;

    /* configure pins for uart usage */
    pin_configure(BANK_0, PIN_2, PIN_FUNCTION_1, PIN_MODE_PULLUP);
    pin_configure(BANK_0, PIN_3, PIN_FUNCTION_1, PIN_MODE_PULLUP);

    /* disable interrupts */
    UART0->IER = IIR_DISABLED;

    /* Line Control Register 8 bits, no Parity, 1 Stop bit, DLAB=1 */
    UART0->LCR = WLS_8BIT | DIVISOR_LATCH_ACCESS;

    /* Set baud rate */
    divider = ((MAIN_FREQUENCY / PCLK_UART0_DIVIDER) / UART_PRESCALER) / baudrate;

    /* Divisor Latch registers */
    UART0->DLM = divider / LATCH_REGISTER_DIVIDER;
    UART0->DLL = divider % LATCH_REGISTER_DIVIDER;

    /* disable latch access */
    UART0->LCR = WLS_8BIT;

    /* Enable and reset TX and RX FIFO */
    UART0->FCR = FIFO_ENABLE | FIFO_RX_RESET | FIFO_TX_RESET;

    /* read IIR to clear interrupts */
    divider = UART0->IIR;

    /* enable interrupt for RX line and rx data available */
    UART0->IER = RX_DATA_AV_INT;

    /* set interrupt priority */
    NVIC_SetPriority(UART0_IRQn, UART0_NVIC_PRIO);

    /* enable NVIC interrupt */
    NVIC_EnableIRQ(UART0_IRQn);

}

/**
 * Read single byte on UART
 *
 * @param  uint8_t *data data byte
 * @return none
 *
 * @brief  read byte from uart0
 *
 */
void uart_read_byte(uint8_t *data)
{
    /* wait for incoming data */
    while ((UART0->LSR & LSR_RDR) == 0)
    {
        ;
    }
    /* read incoming data from register */
    *data = UART0->RBR;
}

/**
 * Send single byte on UART
 *
 * @param  uint8_t data data byte
 * @return none
 *
 * @brief  send byte to uart0
 *
 */
void uart_send_byte(uint8_t data)
{
    if( xSemaphoreTake( xUart_Semaphore, ( portTickType ) 10 ) == pdTRUE )
    {
        /* TX register empty? */
        while ((UART0->LSR & LSR_THRE) == 0)
        {
            ;
        }
        /* write data to send register */
        UART0->THR = data;

        /* input was CR */
        if (data == '\n')
        {
            while (!(UART0->LSR & LSR_THRE))
            {
                ;
            }
            /* respond with CR */
            UART0->THR = 0x0D;
        }

        /* release semaphore */
        xSemaphoreGive( xUart_Semaphore );
    }

}

/**
 * Send uart string
 *
 * @param  uint8_t *data start of string
 * @return none
 *
 * @brief  send string to uart0
 *
 */
void uart_send_str(uint8_t *data)
{
    uint8_t i = 0;

    while (data[i] != '\0')
    {
        uart_send_byte(data[i++]);
    }
}

/**
 * Uart0 interrupt service routine
 *
 * @param  none
 * @return none
 *
 * @brief  handle uart0 interrupts
 *
 */
void uart0_isr(void)
{
    portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
    uint8_t rx_data;

    /* rx fifo empty? */
    while ((UART0->IIR & IIR_PENDING) == 0)
    {
        /* read byte to rx data */
        rx_data = UART0->RBR;

        /* send byte into uart queue */
        xQueueSendToBackFromISR(xUart_Queue, &rx_data ,&xHigherPriorityTaskWoken);

        /* force context switch*/
        portEND_SWITCHING_ISR( xHigherPriorityTaskWoken);
    }


}

/**
 * Print output arguments to uart
 *
 * @param  int8_t *fmt input arguments
 * @return none
 *
 * @brief  convert input variables and write to RS232
 *
 *
 */
void ui_printf(int8_t *fmt, ...)
{
    va_list ap;
    int8_t *p, *sval;
    double ival;
    uint32_t uval;
    int8_t buf[32];

    /* library function */
    va_start(ap, fmt);

    /* make ap point to the first unnamed arg */
    for (p = fmt; *p; p++)
    {
        /* print character, if this is no argument */
        if (*p != '%')
        {
            uart_send_byte(*p);
            continue;
        }
        switch (*++p)
        {
        /* uint32_t */
        case 'u':
            uval = va_arg(ap, int32_t);
            uart_uitoa(uval, buf);
            uart_send_str(buf);
            break;
        /* decimal or integer */
        case 'd':
        case 'i':
            ival = va_arg(ap, int32_t);
            uart_itoa(ival, buf);
            uart_send_str(buf);
            break;
        /* character */
        case 'c':
            ival = va_arg(ap, int32_t);
            uart_send_byte(ival);
            break;
        /* hex value */
        case 'x':
            ival = va_arg(ap, int32_t);
            uart_xtoa(ival, buf);
            uart_send_str(buf);
            break;
        /* print string */
        case 's':
            sval = va_arg(ap, int8_t *);
            uart_send_str(sval);
            break;
        default:
            uart_send_byte(*p);
            break;
        }
    }

    /* library function */
    va_end(ap);
}



/**
 * Convert unsigned int to ascii
 *
 * @param  uint32_t number input number
 * @param  uint8_t* string output value
 * @return none
 *
 * @brief  convert integer to ascii
 *
 */
void uart_uitoa(uint32_t number, int8_t* string)
{
    uint8_t i;

    for (i = 9; i >= 0; i--)
    {
        /* modulo, add offset to ascii 0 */
        string[i] = (number % 10) + '0';
        number /= 10;
    }
    string[10] = '\0';
}



/**
 * Convert hex to ascii
 *
 * @param  uint8_t number input number
 * @param  uint8_t* string output value
 * @return none
 *
 * @brief  convert hex to ascii
 *
 *
 */
void uart_xtoa(uint8_t number, int8_t* string)
{
    uint8_t i;

    string[0] = ((number >> 4) & 0x0F);

    if(string[0] < 0x0A)
    {
        string[0] += '0';
    }
    else
    {
        string[0] += '7';
    }
    string[1] = (number & 0x0F);

    if(string[1] < 0x0A)
    {
        string[1] += '0';
    }
    else
    {
        string[1] += '7';
    }
    string[2] = '\0';


}

/**
 * Convert hex to ascii
 *
 * @param  uint32_t number input number
 * @param  uint8_t* string output value
 * @return none
 *
 * @brief  convert signed integer to ascii
 *
 *
 */
void uart_itoa(int32_t number, int8_t* string)
{
    uint8_t i;

    /* negative value */
    if (number < 0)
    {
        string[0] = '-';
        number = -number;
    }

    else
    {
        /* positive */
        string[0] = ' ';
    }

    for (i = 10; i >= 1; i--)
    {
        /* modulo, add offset to ascii 0 */
        string[i] = (number % 10) + '0';
        number /= 10;
    }
    string[11] = '\0';
}

